{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "e095bcd5",
   "metadata": {},
   "source": [
    "Copyright (c) MONAI Consortium  \n",
    "Licensed under the Apache License, Version 2.0 (the \"License\");  \n",
    "you may not use this file except in compliance with the License.  \n",
    "You may obtain a copy of the License at  \n",
    "&nbsp;&nbsp;&nbsp;&nbsp;http://www.apache.org/licenses/LICENSE-2.0  \n",
    "Unless required by applicable law or agreed to in writing, software  \n",
    "distributed under the License is distributed on an \"AS IS\" BASIS,  \n",
    "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  \n",
    "See the License for the specific language governing permissions and  \n",
    "limitations under the License."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2183ad2f-24e7-467a-9314-e2da8e895811",
   "metadata": {},
   "source": [
    "# Workflow Profiling\n",
    "\n",
    "This notebook discusses the use of the `monai.utils.WorkflowProfiler` used to gather information about time requirements of various parts of your workflow. This will cover assessing the time spent in transforms, generating batches, training forward and backward passes, etc. This class gathers information using tracing about what functions/methods are called as well as marking explicit constructs to profile, such as callables and context blocks. \n",
    "\n",
    "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Project-MONAI/tutorials/blob/main/modules/workflow_profiling.ipynb)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9f8a0471",
   "metadata": {},
   "source": [
    "## Setup environment"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3ed7b77b-b531-4b63-9f86-966829cea404",
   "metadata": {},
   "outputs": [],
   "source": [
    "!python -c \"import monai\" || pip install -q monai-weekly"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f3fa359f",
   "metadata": {},
   "source": [
    "## Setup imports"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "d00de56c-fd3a-413b-acae-8a41c26742b7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "MONAI version: 1.2.0+148.g7dfdcb94.dirty\n",
      "Numpy version: 1.22.2\n",
      "Pytorch version: 2.0.0a0+1767026\n",
      "MONAI flags: HAS_EXT = False, USE_COMPILED = False, USE_META_DICT = False\n",
      "MONAI rev id: 7dfdcb94eb42ac0f3af80f3c8e32421facd536ea\n",
      "MONAI __file__: /workspace/Code/MONAI/monai/__init__.py\n",
      "\n",
      "Optional dependencies:\n",
      "Pytorch Ignite version: 0.4.11\n",
      "ITK version: 5.3.0\n",
      "Nibabel version: 5.1.0\n",
      "scikit-image version: 0.21.0\n",
      "scipy version: 1.10.1\n",
      "Pillow version: 9.2.0\n",
      "Tensorboard version: 2.9.0\n",
      "gdown version: 4.7.1\n",
      "TorchVision version: 0.15.0a0\n",
      "tqdm version: 4.65.0\n",
      "lmdb version: 1.4.1\n",
      "psutil version: 5.9.4\n",
      "pandas version: 1.5.2\n",
      "einops version: 0.6.1\n",
      "transformers version: 4.21.3\n",
      "mlflow version: 2.4.0\n",
      "pynrrd version: 1.0.0\n",
      "clearml version: 1.11.1rc1\n",
      "\n",
      "For details about installing the optional dependencies, please visit:\n",
      "    https://docs.monai.io/en/latest/installation.html#installing-the-recommended-dependencies\n",
      "\n"
     ]
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import monai\n",
    "import monai.transforms as mt\n",
    "import numpy as np\n",
    "import torch\n",
    "from monai.config import print_config\n",
    "from monai.data import DataLoader, Dataset, create_test_image_3d\n",
    "from monai.losses import DiceLoss\n",
    "from monai.networks.nets import UNet\n",
    "from monai.utils import ProfileHandler, WorkflowProfiler\n",
    "from monai.utils.enums import CommonKeys\n",
    "\n",
    "print_config()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ba78581a-38d7-4c0c-9c5d-a5214c05ea19",
   "metadata": {},
   "source": [
    "# Profiling Components\n",
    "\n",
    "The `WorkflowProfiler` defines a context manager which is used to start and stop its internal results reading thread and configure tracing. Typically all profiling should be done with the context otherwise the results may not be read and so won't appear in the summary. Methods of this class are provided to create separate context blocks used to time how long it takes to enter and exit, wrappers around callables to time how long calls take, and iterables to time how long item generation takes.\n",
    "\n",
    "Here the test synthetic dataset is created within such a context to demonstrate using sub-blocks to time parts of a workflow and wrapping callables:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "eb7b7ad8-2c4a-4b77-b9bf-5db9e43d0b6f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'Data generation func': (30, 1.2739437690000004, 0.04246479230000001, 0.003321578522194533, 0.040405682000000005, 0.054325267000000003), 'Generation loop': (1, 1.6376470150000002, 1.6376470150000002, 0.0, 1.6376470150000002, 1.6376470150000002)}\n"
     ]
    }
   ],
   "source": [
    "dataset = []\n",
    "num_items = 30\n",
    "im_shape = (128, 128, 128)\n",
    "\n",
    "# create the profiler using None to disable tracing for now\n",
    "with WorkflowProfiler(call_selector=None) as wp:\n",
    "    # wrap create_test_image_3d in a function which will log how long invocations take\n",
    "    gen_func = wp.profile_callable(\"Data generation func\")(create_test_image_3d)\n",
    "\n",
    "    # enclose code in this context which recalls how long execution took\n",
    "    with wp.profile_ctx(\"Generation loop\"):\n",
    "        for _ in range(num_items):\n",
    "            img, seg = gen_func(*im_shape, num_seg_classes=1)\n",
    "\n",
    "            dataset.append({CommonKeys.IMAGE: img, CommonKeys.LABEL: seg})\n",
    "\n",
    "print(wp.get_times_summary())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8a14fe24-8650-42a2-91e2-2f47390b4d8d",
   "metadata": {},
   "source": [
    "The times summary this produced isn't terribly legible in this form so let's use Pandas to make a nice table in Jupyter:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "c2170469-8c1a-4952-b0b1-bad33e96ac89",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Count</th>\n",
       "      <th>Total Time (s)</th>\n",
       "      <th>Avg</th>\n",
       "      <th>Std</th>\n",
       "      <th>Min</th>\n",
       "      <th>Max</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Generation loop</th>\n",
       "      <td>1</td>\n",
       "      <td>1.637647</td>\n",
       "      <td>1.637647</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>1.637647</td>\n",
       "      <td>1.637647</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Data generation func</th>\n",
       "      <td>30</td>\n",
       "      <td>1.273944</td>\n",
       "      <td>0.042465</td>\n",
       "      <td>0.003322</td>\n",
       "      <td>0.040406</td>\n",
       "      <td>0.054325</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                      Count  Total Time (s)       Avg       Std       Min  \\\n",
       "Generation loop           1        1.637647  1.637647  0.000000  1.637647   \n",
       "Data generation func     30        1.273944  0.042465  0.003322  0.040406   \n",
       "\n",
       "                           Max  \n",
       "Generation loop       1.637647  \n",
       "Data generation func  0.054325  "
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "wp.get_times_summary_pd()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "04939460-281f-43b1-8f1c-ed80f461206c",
   "metadata": {},
   "source": [
    "This gives time summaries for the two constructs that were profiled. The \"Generation loop\" context was entered only once while the \"Data generation func\" (which was `create_test_image_3d` wrapped in another callable) was called multiple times. A context can be entered and exited multiple times, and multiple different context blocks can have the same name thus accumulating results in one place. \n",
    "\n",
    "The raw results can be queried with `get_results` which ensures thread and process safety. This should be called outside the enclosing context to ensure that thread finalisation has taken place before results are gathered. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "8dcada12-6392-441e-b38d-eac3e5f3721b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{'Data generation func': (ProfileResult(name='Data generation func', time=47398909, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:06.023334'), ProfileResult(name='Data generation func', time=42221740, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:06.077886'), ProfileResult(name='Data generation func', time=41500623, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:06.131292'), ProfileResult(name='Data generation func', time=41121253, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:06.184417'), ProfileResult(name='Data generation func', time=42069167, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:06.238471'), ProfileResult(name='Data generation func', time=41670790, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:06.292594'), ProfileResult(name='Data generation func', time=41048405, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:06.345699'), ProfileResult(name='Data generation func', time=40405682, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:06.398108'), ProfileResult(name='Data generation func', time=40587227, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:06.450557'), ProfileResult(name='Data generation func', time=41242960, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:06.503666'), ProfileResult(name='Data generation func', time=43531298, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:06.559175'), ProfileResult(name='Data generation func', time=49959417, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:06.621551'), ProfileResult(name='Data generation func', time=54325267, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:06.688345'), ProfileResult(name='Data generation func', time=49923551, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:06.755242'), ProfileResult(name='Data generation func', time=40717654, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:06.808763'), ProfileResult(name='Data generation func', time=43255896, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:06.863977'), ProfileResult(name='Data generation func', time=41245316, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:06.917262'), ProfileResult(name='Data generation func', time=41239263, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:06.970432'), ProfileResult(name='Data generation func', time=40950528, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:07.023269'), ProfileResult(name='Data generation func', time=41045447, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:07.076219'), ProfileResult(name='Data generation func', time=40896448, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:07.129137'), ProfileResult(name='Data generation func', time=40598612, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:07.182182'), ProfileResult(name='Data generation func', time=41223545, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:07.235351'), ProfileResult(name='Data generation func', time=40485273, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:07.287878'), ProfileResult(name='Data generation func', time=41270315, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:07.340988'), ProfileResult(name='Data generation func', time=40874147, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:07.393861'), ProfileResult(name='Data generation func', time=41208147, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:07.447149'), ProfileResult(name='Data generation func', time=41095581, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:07.500376'), ProfileResult(name='Data generation func', time=40410115, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:07.552636'), ProfileResult(name='Data generation func', time=40421193, filename='/usr/lib/python3.8/contextlib.py', lineno=74, pid=1480146, timestamp='2023-09-25 08:08:07.604815')), 'Generation loop': (ProfileResult(name='Generation loop', time=1637647015, filename='/tmp/ipykernel_1480146/2799409184.py', lineno=11, pid=1480146, timestamp='2023-09-25 08:08:07.604940'),)}\n"
     ]
    }
   ],
   "source": [
    "results = wp.get_results()\n",
    "print(results)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e02baa0e-aa6b-4c0f-bf25-284612924d06",
   "metadata": {},
   "source": [
    "The result is a dictionary relating construct names to a list of invocations. Each call or execution of a construct produces a `ProfileResult` object to store information about the call, specifically `time` records how many nanoseconds were taken. Stack inspection and tracing is used to, as best as possible, record where the invocation was in code. The PID is also recorded since results can be collected from subprocesses when `DataLoader` objects are used with process workers. \n",
    "\n",
    "## Transform Profiling\n",
    "\n",
    "Profiling how long each transform takes when executing a pipeline is complex owing to difficulting in determining when a transform runs, what times are taken, what transforms call others, etc. The `WorkflowProfiler` class uses tracing to profile all function/method calls matching a given criteria. In the previous example the criteria function was `None` to disable tracing, however the default is a function which selects only the `__call__` methods of `Transforms` instances. This includes top-level transforms and those called by others, but gives a useful idea of time taken relative to one another. Note that tracing is expensive so the times given are not real-world times but are still useful to identify which transform is expensive. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "0b1f17ec-242c-4f02-979c-e494636ea8ae",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([1, 128, 128, 128]) torch.float32\n",
      "torch.Size([1, 128, 128, 128]) torch.float32\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<matplotlib.image.AxesImage at 0x7f7acad7e400>"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 640x480 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "both_keys = (CommonKeys.IMAGE, CommonKeys.LABEL)\n",
    "rand_prob = 0.5\n",
    "\n",
    "trans = [\n",
    "    mt.EnsureChannelFirstd(both_keys, channel_dim=\"no_channel\"),\n",
    "    mt.RandSpatialCropd(both_keys, (96, 96, 96), random_size=False),\n",
    "    mt.ScaleIntensityd(CommonKeys.IMAGE),\n",
    "    mt.RandAxisFlipd(both_keys, rand_prob),\n",
    "    mt.RandRotate90d(both_keys, prob=rand_prob, spatial_axes=[0, 1]),\n",
    "    mt.RandSmoothDeformd(\n",
    "        keys=both_keys,\n",
    "        spatial_size=im_shape,\n",
    "        rand_size=(3, 3, 3),\n",
    "        pad=2,\n",
    "        def_range=0.1,\n",
    "        field_mode=monai.utils.InterpolateMode.TRILINEAR,\n",
    "        grid_mode=(monai.utils.GridSampleMode.BILINEAR, monai.utils.GridSampleMode.NEAREST),\n",
    "        align_corners=True,\n",
    "        prob=1.0,\n",
    "    ),\n",
    "    mt.EnsureTyped(both_keys, dtype=(np.float32, np.int32)),\n",
    "]\n",
    "\n",
    "train_trans = mt.Compose(trans)\n",
    "\n",
    "with WorkflowProfiler() as wp:\n",
    "    test = train_trans(dataset[0])\n",
    "\n",
    "print(test[CommonKeys.IMAGE].shape, test[CommonKeys.IMAGE].dtype)\n",
    "print(test[CommonKeys.LABEL].shape, test[CommonKeys.LABEL].dtype)\n",
    "\n",
    "fig, (ax0, ax1) = plt.subplots(1, 2)\n",
    "ax0.imshow(test[CommonKeys.IMAGE][0, 48], interpolation=\"none\")\n",
    "ax1.imshow(test[CommonKeys.LABEL][0, 48], interpolation=\"none\", cmap=\"gray\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1dba67da-16fc-4d51-8116-d4ba57e8dcfa",
   "metadata": {},
   "source": [
    "The results is now populated with calls to the `__call__` methods of every transform mentioned above and all those they use. Although tracing has high overhead this still demonstrates that `RandSmoothDeformd` (which uses `RandSmoothDeform`) is the slowest transform defined above by a large margin. Many transforms have very fast total time if by chance they were skipped since their `prob` arguments are less than 1.0. For multiple runs of the same sequence, ie. when generating a whole batch, the results will be more indicative of the time proportion each transform uses. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "f89d7b69-b304-4ec2-bfbd-de96ae0fab47",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Count</th>\n",
       "      <th>Total Time (s)</th>\n",
       "      <th>Avg</th>\n",
       "      <th>Std</th>\n",
       "      <th>Min</th>\n",
       "      <th>Max</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Compose.__call__</th>\n",
       "      <td>1</td>\n",
       "      <td>0.117255</td>\n",
       "      <td>0.117255</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>0.117255</td>\n",
       "      <td>0.117255</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RandSmoothDeformd.__call__</th>\n",
       "      <td>1</td>\n",
       "      <td>0.104854</td>\n",
       "      <td>0.104854</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>0.104854</td>\n",
       "      <td>0.104854</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RandSmoothDeform.__call__</th>\n",
       "      <td>2</td>\n",
       "      <td>0.104426</td>\n",
       "      <td>0.052213</td>\n",
       "      <td>0.014874</td>\n",
       "      <td>0.037340</td>\n",
       "      <td>0.067087</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RandSpatialCrop.__call__</th>\n",
       "      <td>4</td>\n",
       "      <td>0.006386</td>\n",
       "      <td>0.001597</td>\n",
       "      <td>0.000366</td>\n",
       "      <td>0.001166</td>\n",
       "      <td>0.002016</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RandSpatialCropd.__call__</th>\n",
       "      <td>1</td>\n",
       "      <td>0.003592</td>\n",
       "      <td>0.003592</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>0.003592</td>\n",
       "      <td>0.003592</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RandRotate90d.__call__</th>\n",
       "      <td>1</td>\n",
       "      <td>0.003427</td>\n",
       "      <td>0.003427</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>0.003427</td>\n",
       "      <td>0.003427</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Rotate90.__call__</th>\n",
       "      <td>2</td>\n",
       "      <td>0.002973</td>\n",
       "      <td>0.001486</td>\n",
       "      <td>0.000061</td>\n",
       "      <td>0.001425</td>\n",
       "      <td>0.001547</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>ScaleIntensityd.__call__</th>\n",
       "      <td>1</td>\n",
       "      <td>0.001870</td>\n",
       "      <td>0.001870</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>0.001870</td>\n",
       "      <td>0.001870</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>ScaleIntensity.__call__</th>\n",
       "      <td>1</td>\n",
       "      <td>0.001776</td>\n",
       "      <td>0.001776</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>0.001776</td>\n",
       "      <td>0.001776</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>EnsureChannelFirstd.__call__</th>\n",
       "      <td>1</td>\n",
       "      <td>0.001445</td>\n",
       "      <td>0.001445</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>0.001445</td>\n",
       "      <td>0.001445</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>EnsureChannelFirst.__call__</th>\n",
       "      <td>2</td>\n",
       "      <td>0.001199</td>\n",
       "      <td>0.000599</td>\n",
       "      <td>0.000326</td>\n",
       "      <td>0.000273</td>\n",
       "      <td>0.000926</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RandAxisFlipd.__call__</th>\n",
       "      <td>1</td>\n",
       "      <td>0.000679</td>\n",
       "      <td>0.000679</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>0.000679</td>\n",
       "      <td>0.000679</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>EnsureTyped.__call__</th>\n",
       "      <td>1</td>\n",
       "      <td>0.000655</td>\n",
       "      <td>0.000655</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>0.000655</td>\n",
       "      <td>0.000655</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>EnsureType.__call__</th>\n",
       "      <td>2</td>\n",
       "      <td>0.000559</td>\n",
       "      <td>0.000279</td>\n",
       "      <td>0.000073</td>\n",
       "      <td>0.000207</td>\n",
       "      <td>0.000352</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                              Count  Total Time (s)       Avg       Std  \\\n",
       "Compose.__call__                  1        0.117255  0.117255  0.000000   \n",
       "RandSmoothDeformd.__call__        1        0.104854  0.104854  0.000000   \n",
       "RandSmoothDeform.__call__         2        0.104426  0.052213  0.014874   \n",
       "RandSpatialCrop.__call__          4        0.006386  0.001597  0.000366   \n",
       "RandSpatialCropd.__call__         1        0.003592  0.003592  0.000000   \n",
       "RandRotate90d.__call__            1        0.003427  0.003427  0.000000   \n",
       "Rotate90.__call__                 2        0.002973  0.001486  0.000061   \n",
       "ScaleIntensityd.__call__          1        0.001870  0.001870  0.000000   \n",
       "ScaleIntensity.__call__           1        0.001776  0.001776  0.000000   \n",
       "EnsureChannelFirstd.__call__      1        0.001445  0.001445  0.000000   \n",
       "EnsureChannelFirst.__call__       2        0.001199  0.000599  0.000326   \n",
       "RandAxisFlipd.__call__            1        0.000679  0.000679  0.000000   \n",
       "EnsureTyped.__call__              1        0.000655  0.000655  0.000000   \n",
       "EnsureType.__call__               2        0.000559  0.000279  0.000073   \n",
       "\n",
       "                                   Min       Max  \n",
       "Compose.__call__              0.117255  0.117255  \n",
       "RandSmoothDeformd.__call__    0.104854  0.104854  \n",
       "RandSmoothDeform.__call__     0.037340  0.067087  \n",
       "RandSpatialCrop.__call__      0.001166  0.002016  \n",
       "RandSpatialCropd.__call__     0.003592  0.003592  \n",
       "RandRotate90d.__call__        0.003427  0.003427  \n",
       "Rotate90.__call__             0.001425  0.001547  \n",
       "ScaleIntensityd.__call__      0.001870  0.001870  \n",
       "ScaleIntensity.__call__       0.001776  0.001776  \n",
       "EnsureChannelFirstd.__call__  0.001445  0.001445  \n",
       "EnsureChannelFirst.__call__   0.000273  0.000926  \n",
       "RandAxisFlipd.__call__        0.000679  0.000679  \n",
       "EnsureTyped.__call__          0.000655  0.000655  \n",
       "EnsureType.__call__           0.000207  0.000352  "
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "wp.get_times_summary_pd()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "72660f29-be0e-4ea7-9736-394c0d4e690a",
   "metadata": {},
   "source": [
    "The call selection function can be substituted with any callable which accepts a `frame` object used by `sys.settrace` and `threading.settrace` and returns `True` if the call the frame represents should be traced. The default inspects the frame and returns true if the call is `__call__` and the receiver is a `Transform` instance:\n",
    "\n",
    "```python\n",
    "def select_transform_call(frame):\n",
    "    \"\"\"Returns True if `frame` is a call to a `Transform` object's `_call__` method.\"\"\"\n",
    "    from monai.transforms import Transform  # prevents circular import\n",
    "\n",
    "    self_obj = frame.f_locals.get(\"self\", None)\n",
    "    return frame.f_code.co_name == \"__call__\" and isinstance(self_obj, Transform)\n",
    "```\n",
    "\n",
    "You can provide your own criteria functions to replace this one, just don't provide one which traces all calls since this will lead to infinite recursion and be incredibly slow even if that was avoided.\n",
    "\n",
    "An alternative function to trace only nominated objects may be more useful to narrow the scope of the trace to top-level transforms only: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "96b0f68b-9a34-4714-b305-0395835ddeb0",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Count</th>\n",
       "      <th>Total Time (s)</th>\n",
       "      <th>Avg</th>\n",
       "      <th>Std</th>\n",
       "      <th>Min</th>\n",
       "      <th>Max</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>RandSmoothDeformd.__call__</th>\n",
       "      <td>1</td>\n",
       "      <td>0.092891</td>\n",
       "      <td>0.092891</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.092891</td>\n",
       "      <td>0.092891</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RandSpatialCropd.__call__</th>\n",
       "      <td>1</td>\n",
       "      <td>0.006538</td>\n",
       "      <td>0.006538</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.006538</td>\n",
       "      <td>0.006538</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>ScaleIntensityd.__call__</th>\n",
       "      <td>1</td>\n",
       "      <td>0.004565</td>\n",
       "      <td>0.004565</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.004565</td>\n",
       "      <td>0.004565</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RandAxisFlipd.__call__</th>\n",
       "      <td>1</td>\n",
       "      <td>0.002347</td>\n",
       "      <td>0.002347</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.002347</td>\n",
       "      <td>0.002347</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RandRotate90d.__call__</th>\n",
       "      <td>1</td>\n",
       "      <td>0.001494</td>\n",
       "      <td>0.001494</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.001494</td>\n",
       "      <td>0.001494</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>EnsureChannelFirstd.__call__</th>\n",
       "      <td>1</td>\n",
       "      <td>0.000542</td>\n",
       "      <td>0.000542</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.000542</td>\n",
       "      <td>0.000542</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>EnsureTyped.__call__</th>\n",
       "      <td>1</td>\n",
       "      <td>0.000367</td>\n",
       "      <td>0.000367</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.000367</td>\n",
       "      <td>0.000367</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                              Count  Total Time (s)       Avg  Std       Min  \\\n",
       "RandSmoothDeformd.__call__        1        0.092891  0.092891  0.0  0.092891   \n",
       "RandSpatialCropd.__call__         1        0.006538  0.006538  0.0  0.006538   \n",
       "ScaleIntensityd.__call__          1        0.004565  0.004565  0.0  0.004565   \n",
       "RandAxisFlipd.__call__            1        0.002347  0.002347  0.0  0.002347   \n",
       "RandRotate90d.__call__            1        0.001494  0.001494  0.0  0.001494   \n",
       "EnsureChannelFirstd.__call__      1        0.000542  0.000542  0.0  0.000542   \n",
       "EnsureTyped.__call__              1        0.000367  0.000367  0.0  0.000367   \n",
       "\n",
       "                                   Max  \n",
       "RandSmoothDeformd.__call__    0.092891  \n",
       "RandSpatialCropd.__call__     0.006538  \n",
       "ScaleIntensityd.__call__      0.004565  \n",
       "RandAxisFlipd.__call__        0.002347  \n",
       "RandRotate90d.__call__        0.001494  \n",
       "EnsureChannelFirstd.__call__  0.000542  \n",
       "EnsureTyped.__call__          0.000367  "
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "class TraceObjectsOnly:\n",
    "    def __init__(self, objects):\n",
    "        self.objects = objects\n",
    "\n",
    "    def __call__(self, frame):\n",
    "        self_obj = frame.f_locals.get(\"self\", None)\n",
    "        return frame.f_code.co_name == \"__call__\" and self_obj in self.objects\n",
    "\n",
    "\n",
    "# `trans` is the list of transforms defined above, not the Compose object\n",
    "with WorkflowProfiler(TraceObjectsOnly(trans)) as wp:\n",
    "    test = train_trans(dataset[0])\n",
    "\n",
    "wp.get_times_summary_pd()  # much more concise view of what's going on"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "489b43b0-4930-467e-b519-2e6bd98b96d6",
   "metadata": {},
   "source": [
    "## Whole Workflow Profiling\n",
    "\n",
    "These elements can now be combined to profile all the aspects of the typically Pytorch training workflow:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "490387f9-d59b-4862-8174-63591341114d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1\n",
      "0.632347047328949\n",
      "0.6424596905708313\n",
      "0.5509625673294067\n",
      "0.4700535833835602\n",
      "0.5107573866844177\n",
      "0.512077271938324\n",
      "Epoch 2\n",
      "0.4802013337612152\n",
      "0.5469694137573242\n",
      "0.4922555088996887\n",
      "0.4222610890865326\n",
      "0.4443279206752777\n",
      "0.45488134026527405\n",
      "Epoch 3\n",
      "0.4441751539707184\n",
      "0.5135425925254822\n",
      "0.46540212631225586\n",
      "0.3792388141155243\n",
      "0.4192323386669159\n",
      "0.4318554103374481\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Count</th>\n",
       "      <th>Total Time (s)</th>\n",
       "      <th>Avg</th>\n",
       "      <th>Std</th>\n",
       "      <th>Min</th>\n",
       "      <th>Max</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>RandSmoothDeformd.__call__</th>\n",
       "      <td>90</td>\n",
       "      <td>21.831027</td>\n",
       "      <td>0.242567</td>\n",
       "      <td>0.020258</td>\n",
       "      <td>0.203764</td>\n",
       "      <td>0.304642</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Epoch</th>\n",
       "      <td>3</td>\n",
       "      <td>8.985920</td>\n",
       "      <td>2.995307</td>\n",
       "      <td>0.087524</td>\n",
       "      <td>2.927028</td>\n",
       "      <td>3.118856</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Batch generation</th>\n",
       "      <td>18</td>\n",
       "      <td>5.026321</td>\n",
       "      <td>0.279240</td>\n",
       "      <td>0.463398</td>\n",
       "      <td>0.000062</td>\n",
       "      <td>1.337905</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>forward_pass</th>\n",
       "      <td>18</td>\n",
       "      <td>0.454474</td>\n",
       "      <td>0.025249</td>\n",
       "      <td>0.031146</td>\n",
       "      <td>0.015030</td>\n",
       "      <td>0.153217</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>ScaleIntensityd.__call__</th>\n",
       "      <td>90</td>\n",
       "      <td>0.380329</td>\n",
       "      <td>0.004226</td>\n",
       "      <td>0.001956</td>\n",
       "      <td>0.002278</td>\n",
       "      <td>0.010741</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>backward_pass</th>\n",
       "      <td>18</td>\n",
       "      <td>0.219559</td>\n",
       "      <td>0.012198</td>\n",
       "      <td>0.037680</td>\n",
       "      <td>0.002481</td>\n",
       "      <td>0.167538</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RandRotate90d.__call__</th>\n",
       "      <td>90</td>\n",
       "      <td>0.145601</td>\n",
       "      <td>0.001618</td>\n",
       "      <td>0.002447</td>\n",
       "      <td>0.000260</td>\n",
       "      <td>0.020887</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>EnsureTyped.__call__</th>\n",
       "      <td>90</td>\n",
       "      <td>0.136556</td>\n",
       "      <td>0.001517</td>\n",
       "      <td>0.000452</td>\n",
       "      <td>0.000841</td>\n",
       "      <td>0.002603</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RandAxisFlipd.__call__</th>\n",
       "      <td>90</td>\n",
       "      <td>0.124048</td>\n",
       "      <td>0.001378</td>\n",
       "      <td>0.001194</td>\n",
       "      <td>0.000299</td>\n",
       "      <td>0.005235</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RandSpatialCropd.__call__</th>\n",
       "      <td>90</td>\n",
       "      <td>0.121727</td>\n",
       "      <td>0.001353</td>\n",
       "      <td>0.000433</td>\n",
       "      <td>0.001055</td>\n",
       "      <td>0.003184</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>EnsureChannelFirstd.__call__</th>\n",
       "      <td>90</td>\n",
       "      <td>0.043690</td>\n",
       "      <td>0.000485</td>\n",
       "      <td>0.000320</td>\n",
       "      <td>0.000266</td>\n",
       "      <td>0.001583</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Create DataLoader</th>\n",
       "      <td>1</td>\n",
       "      <td>0.000189</td>\n",
       "      <td>0.000189</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>0.000189</td>\n",
       "      <td>0.000189</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                              Count  Total Time (s)       Avg       Std  \\\n",
       "RandSmoothDeformd.__call__       90       21.831027  0.242567  0.020258   \n",
       "Epoch                             3        8.985920  2.995307  0.087524   \n",
       "Batch generation                 18        5.026321  0.279240  0.463398   \n",
       "forward_pass                     18        0.454474  0.025249  0.031146   \n",
       "ScaleIntensityd.__call__         90        0.380329  0.004226  0.001956   \n",
       "backward_pass                    18        0.219559  0.012198  0.037680   \n",
       "RandRotate90d.__call__           90        0.145601  0.001618  0.002447   \n",
       "EnsureTyped.__call__             90        0.136556  0.001517  0.000452   \n",
       "RandAxisFlipd.__call__           90        0.124048  0.001378  0.001194   \n",
       "RandSpatialCropd.__call__        90        0.121727  0.001353  0.000433   \n",
       "EnsureChannelFirstd.__call__     90        0.043690  0.000485  0.000320   \n",
       "Create DataLoader                 1        0.000189  0.000189  0.000000   \n",
       "\n",
       "                                   Min       Max  \n",
       "RandSmoothDeformd.__call__    0.203764  0.304642  \n",
       "Epoch                         2.927028  3.118856  \n",
       "Batch generation              0.000062  1.337905  \n",
       "forward_pass                  0.015030  0.153217  \n",
       "ScaleIntensityd.__call__      0.002278  0.010741  \n",
       "backward_pass                 0.002481  0.167538  \n",
       "RandRotate90d.__call__        0.000260  0.020887  \n",
       "EnsureTyped.__call__          0.000841  0.002603  \n",
       "RandAxisFlipd.__call__        0.000299  0.005235  \n",
       "RandSpatialCropd.__call__     0.001055  0.003184  \n",
       "EnsureChannelFirstd.__call__  0.000266  0.001583  \n",
       "Create DataLoader             0.000189  0.000189  "
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "device = torch.device(\"cuda:0\")\n",
    "net = UNet(\n",
    "    spatial_dims=3,\n",
    "    in_channels=1,\n",
    "    out_channels=1,\n",
    "    channels=(16, 32, 64, 128, 256),\n",
    "    strides=(2, 2, 2, 2),\n",
    "    num_res_units=2,\n",
    ").to(device)\n",
    "\n",
    "loss_func = DiceLoss(sigmoid=True)\n",
    "lr = 1e-3\n",
    "opt = torch.optim.Adam(net.parameters(), lr)\n",
    "num_epochs = 3\n",
    "batch_size = 5\n",
    "\n",
    "\n",
    "with WorkflowProfiler(TraceObjectsOnly(trans)) as wp:\n",
    "    # profile how long it takes to create the dataset and loader, this could be long for different dataset types\n",
    "    with wp.profile_ctx(\"Create DataLoader\"):\n",
    "        ds = Dataset(dataset, train_trans)\n",
    "        dl = DataLoader(ds, batch_size=batch_size, num_workers=4)\n",
    "\n",
    "    # break passes into functions which can be decorated, context blocks in the training loop could be used instead\n",
    "\n",
    "    @wp.profile_callable()\n",
    "    def forward_pass(batch):\n",
    "        img = batch[CommonKeys.IMAGE].to(device)\n",
    "        seg = batch[CommonKeys.LABEL].to(device)\n",
    "\n",
    "        return net(img), seg\n",
    "\n",
    "    @wp.profile_callable()\n",
    "    def backward_pass(pred, seg):\n",
    "        loss = loss_func(pred, seg)\n",
    "        loss.backward()\n",
    "        return loss\n",
    "\n",
    "    for epoch in range(1, 1 + num_epochs):\n",
    "        print(\"Epoch\", epoch)\n",
    "\n",
    "        # allows measuring how long each epoch takes\n",
    "        with wp.profile_ctx(\"Epoch\"):\n",
    "            # measures how long batch generation takes\n",
    "            for batch in wp.profile_iter(\"Batch generation\", dl):\n",
    "                opt.zero_grad()\n",
    "                pred, seg = forward_pass(batch)\n",
    "                loss = backward_pass(pred, seg)\n",
    "                opt.step()\n",
    "                print(loss.item())\n",
    "\n",
    "wp.get_times_summary_pd()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c36cf9d6-3656-4195-a897-fe28f0dfe4fc",
   "metadata": {},
   "source": [
    "Workflows using Ignite Engine classes can be profiled as well to get very similar results by using `ProfileHandler` to track certain pairs of events. In this example batch generation, iteration, and epoch time are measured using this handler in place of iterator and callable profiling:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "85374f79-9a56-4981-9507-f00ee16ca7b4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Count</th>\n",
       "      <th>Total Time (s)</th>\n",
       "      <th>Avg</th>\n",
       "      <th>Std</th>\n",
       "      <th>Min</th>\n",
       "      <th>Max</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>Compose.__call__</th>\n",
       "      <td>90</td>\n",
       "      <td>22.663719</td>\n",
       "      <td>0.251819</td>\n",
       "      <td>0.027767</td>\n",
       "      <td>0.209441</td>\n",
       "      <td>0.304443</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RandSmoothDeformd.__call__</th>\n",
       "      <td>90</td>\n",
       "      <td>21.457371</td>\n",
       "      <td>0.238415</td>\n",
       "      <td>0.025981</td>\n",
       "      <td>0.200652</td>\n",
       "      <td>0.283412</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RandSmoothDeform.__call__</th>\n",
       "      <td>180</td>\n",
       "      <td>21.422791</td>\n",
       "      <td>0.119016</td>\n",
       "      <td>0.017444</td>\n",
       "      <td>0.088241</td>\n",
       "      <td>0.152806</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Epoch</th>\n",
       "      <td>3</td>\n",
       "      <td>8.840104</td>\n",
       "      <td>2.946701</td>\n",
       "      <td>0.024437</td>\n",
       "      <td>2.914117</td>\n",
       "      <td>2.972967</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Batch gen</th>\n",
       "      <td>18</td>\n",
       "      <td>5.106108</td>\n",
       "      <td>0.283673</td>\n",
       "      <td>0.499145</td>\n",
       "      <td>0.000148</td>\n",
       "      <td>1.463968</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Iteration</th>\n",
       "      <td>18</td>\n",
       "      <td>2.140077</td>\n",
       "      <td>0.118893</td>\n",
       "      <td>0.007366</td>\n",
       "      <td>0.111051</td>\n",
       "      <td>0.135437</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Decollated.__call__</th>\n",
       "      <td>36</td>\n",
       "      <td>1.553105</td>\n",
       "      <td>0.043142</td>\n",
       "      <td>0.042523</td>\n",
       "      <td>0.000553</td>\n",
       "      <td>0.088441</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>ScaleIntensityd.__call__</th>\n",
       "      <td>90</td>\n",
       "      <td>0.382803</td>\n",
       "      <td>0.004253</td>\n",
       "      <td>0.001630</td>\n",
       "      <td>0.002289</td>\n",
       "      <td>0.009115</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>ScaleIntensity.__call__</th>\n",
       "      <td>90</td>\n",
       "      <td>0.371928</td>\n",
       "      <td>0.004133</td>\n",
       "      <td>0.001590</td>\n",
       "      <td>0.002229</td>\n",
       "      <td>0.008981</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RandSpatialCrop.__call__</th>\n",
       "      <td>360</td>\n",
       "      <td>0.326454</td>\n",
       "      <td>0.000907</td>\n",
       "      <td>0.001054</td>\n",
       "      <td>0.000493</td>\n",
       "      <td>0.007946</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RandSpatialCropd.__call__</th>\n",
       "      <td>90</td>\n",
       "      <td>0.203022</td>\n",
       "      <td>0.002256</td>\n",
       "      <td>0.001552</td>\n",
       "      <td>0.001441</td>\n",
       "      <td>0.008884</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>EnsureTyped.__call__</th>\n",
       "      <td>90</td>\n",
       "      <td>0.163231</td>\n",
       "      <td>0.001814</td>\n",
       "      <td>0.000902</td>\n",
       "      <td>0.000988</td>\n",
       "      <td>0.008426</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RandRotate90d.__call__</th>\n",
       "      <td>90</td>\n",
       "      <td>0.162323</td>\n",
       "      <td>0.001804</td>\n",
       "      <td>0.001714</td>\n",
       "      <td>0.000336</td>\n",
       "      <td>0.008014</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RandAxisFlipd.__call__</th>\n",
       "      <td>90</td>\n",
       "      <td>0.155694</td>\n",
       "      <td>0.001730</td>\n",
       "      <td>0.001471</td>\n",
       "      <td>0.000383</td>\n",
       "      <td>0.005748</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>EnsureType.__call__</th>\n",
       "      <td>180</td>\n",
       "      <td>0.149147</td>\n",
       "      <td>0.000829</td>\n",
       "      <td>0.000845</td>\n",
       "      <td>0.000108</td>\n",
       "      <td>0.006094</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>RandAxisFlip.__call__</th>\n",
       "      <td>82</td>\n",
       "      <td>0.116219</td>\n",
       "      <td>0.001417</td>\n",
       "      <td>0.000495</td>\n",
       "      <td>0.000828</td>\n",
       "      <td>0.003644</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Rotate90.__call__</th>\n",
       "      <td>82</td>\n",
       "      <td>0.114737</td>\n",
       "      <td>0.001399</td>\n",
       "      <td>0.000684</td>\n",
       "      <td>0.000905</td>\n",
       "      <td>0.005880</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Flip.__call__</th>\n",
       "      <td>82</td>\n",
       "      <td>0.099686</td>\n",
       "      <td>0.001216</td>\n",
       "      <td>0.000362</td>\n",
       "      <td>0.000703</td>\n",
       "      <td>0.002142</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>EnsureChannelFirstd.__call__</th>\n",
       "      <td>90</td>\n",
       "      <td>0.093650</td>\n",
       "      <td>0.001041</td>\n",
       "      <td>0.001154</td>\n",
       "      <td>0.000419</td>\n",
       "      <td>0.006237</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>EnsureChannelFirst.__call__</th>\n",
       "      <td>180</td>\n",
       "      <td>0.067895</td>\n",
       "      <td>0.000377</td>\n",
       "      <td>0.000735</td>\n",
       "      <td>0.000139</td>\n",
       "      <td>0.005878</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>Create DataLoader</th>\n",
       "      <td>1</td>\n",
       "      <td>0.000120</td>\n",
       "      <td>0.000120</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>0.000120</td>\n",
       "      <td>0.000120</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                              Count  Total Time (s)       Avg       Std  \\\n",
       "Compose.__call__                 90       22.663719  0.251819  0.027767   \n",
       "RandSmoothDeformd.__call__       90       21.457371  0.238415  0.025981   \n",
       "RandSmoothDeform.__call__       180       21.422791  0.119016  0.017444   \n",
       "Epoch                             3        8.840104  2.946701  0.024437   \n",
       "Batch gen                        18        5.106108  0.283673  0.499145   \n",
       "Iteration                        18        2.140077  0.118893  0.007366   \n",
       "Decollated.__call__              36        1.553105  0.043142  0.042523   \n",
       "ScaleIntensityd.__call__         90        0.382803  0.004253  0.001630   \n",
       "ScaleIntensity.__call__          90        0.371928  0.004133  0.001590   \n",
       "RandSpatialCrop.__call__        360        0.326454  0.000907  0.001054   \n",
       "RandSpatialCropd.__call__        90        0.203022  0.002256  0.001552   \n",
       "EnsureTyped.__call__             90        0.163231  0.001814  0.000902   \n",
       "RandRotate90d.__call__           90        0.162323  0.001804  0.001714   \n",
       "RandAxisFlipd.__call__           90        0.155694  0.001730  0.001471   \n",
       "EnsureType.__call__             180        0.149147  0.000829  0.000845   \n",
       "RandAxisFlip.__call__            82        0.116219  0.001417  0.000495   \n",
       "Rotate90.__call__                82        0.114737  0.001399  0.000684   \n",
       "Flip.__call__                    82        0.099686  0.001216  0.000362   \n",
       "EnsureChannelFirstd.__call__     90        0.093650  0.001041  0.001154   \n",
       "EnsureChannelFirst.__call__     180        0.067895  0.000377  0.000735   \n",
       "Create DataLoader                 1        0.000120  0.000120  0.000000   \n",
       "\n",
       "                                   Min       Max  \n",
       "Compose.__call__              0.209441  0.304443  \n",
       "RandSmoothDeformd.__call__    0.200652  0.283412  \n",
       "RandSmoothDeform.__call__     0.088241  0.152806  \n",
       "Epoch                         2.914117  2.972967  \n",
       "Batch gen                     0.000148  1.463968  \n",
       "Iteration                     0.111051  0.135437  \n",
       "Decollated.__call__           0.000553  0.088441  \n",
       "ScaleIntensityd.__call__      0.002289  0.009115  \n",
       "ScaleIntensity.__call__       0.002229  0.008981  \n",
       "RandSpatialCrop.__call__      0.000493  0.007946  \n",
       "RandSpatialCropd.__call__     0.001441  0.008884  \n",
       "EnsureTyped.__call__          0.000988  0.008426  \n",
       "RandRotate90d.__call__        0.000336  0.008014  \n",
       "RandAxisFlipd.__call__        0.000383  0.005748  \n",
       "EnsureType.__call__           0.000108  0.006094  \n",
       "RandAxisFlip.__call__         0.000828  0.003644  \n",
       "Rotate90.__call__             0.000905  0.005880  \n",
       "Flip.__call__                 0.000703  0.002142  \n",
       "EnsureChannelFirstd.__call__  0.000419  0.006237  \n",
       "EnsureChannelFirst.__call__   0.000139  0.005878  \n",
       "Create DataLoader             0.000120  0.000120  "
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from monai.engines import SupervisedTrainer  # noqa\n",
    "from ignite.engine import Events  # noqa\n",
    "\n",
    "device = torch.device(\"cuda:0\")\n",
    "net = UNet(\n",
    "    spatial_dims=3,\n",
    "    in_channels=1,\n",
    "    out_channels=1,\n",
    "    channels=(16, 32, 64, 128, 256),\n",
    "    strides=(2, 2, 2, 2),\n",
    "    num_res_units=2,\n",
    ").to(device)\n",
    "\n",
    "loss_func = DiceLoss(sigmoid=True)\n",
    "lr = 1e-3\n",
    "opt = torch.optim.Adam(net.parameters(), lr)\n",
    "num_epochs = 3\n",
    "batch_size = 5\n",
    "\n",
    "with WorkflowProfiler() as wp:\n",
    "    with wp.profile_ctx(\"Create DataLoader\"):\n",
    "        ds = Dataset(dataset, train_trans)\n",
    "        dl = DataLoader(ds, batch_size=batch_size, num_workers=4)\n",
    "\n",
    "    trainer = SupervisedTrainer(\n",
    "        device=device,\n",
    "        max_epochs=num_epochs,\n",
    "        train_data_loader=dl,\n",
    "        network=net,\n",
    "        optimizer=opt,\n",
    "        loss_function=loss_func,\n",
    "    )\n",
    "\n",
    "    epoch_h = ProfileHandler(\"Epoch\", wp, Events.EPOCH_STARTED, Events.EPOCH_COMPLETED).attach(trainer)\n",
    "    iter_h = ProfileHandler(\"Iteration\", wp, Events.ITERATION_STARTED, Events.ITERATION_COMPLETED).attach(trainer)\n",
    "    batch_h = ProfileHandler(\"Batch gen\", wp, Events.GET_BATCH_STARTED, Events.GET_BATCH_COMPLETED).attach(trainer)\n",
    "\n",
    "    trainer.run()\n",
    "\n",
    "wp.get_times_summary_pd()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
